package com.example.apiversioncontroldemo.util.vcon;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.servlet.handler.AbstractHandlerMethodMapping;
import org.springframework.web.servlet.mvc.condition.RequestCondition;

import javax.servlet.http.HttpServletRequest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 请求条件可以通过combine(Object)组合，通过getMatchingCondition(HttpServletRequest)与请求匹配，
 * 并通过compareTo(Object, HttpServletRequest)相互比较，以确定哪个更匹配给定的请求
 *
 * @see AbstractHandlerMethodMapping#lookupHandlerMethod(String, HttpServletRequest) lookupHandlerMethod() 405行，414行
 *
 * @author chen.cr
 * @since 2023/10/19
 */
@Slf4j
public class ApiVersionCondition implements RequestCondition<ApiVersionCondition> {


    @Getter
    private final int apiVersion;
    @Getter
    private final int allowedMinVersion;

    public ApiVersionCondition(int apiVersion, int allowedMinVersion) {
        this.apiVersion = apiVersion;
        this.allowedMinVersion = allowedMinVersion;
    }

    @Override
    public ApiVersionCondition combine(ApiVersionCondition other) {
        return new ApiVersionCondition(other.getApiVersion(), getAllowedMinVersion());
    }

    /**
     * 请求与条件匹配，则返回条件；不匹配则返回null
     *
     * @param request 请求实例
     * @return
     */
    @Override
    public ApiVersionCondition getMatchingCondition(HttpServletRequest request) {
        Integer version = RequestUtils.findApiVersion(request);
        if (version != null) {
            // 最小可兼容版本校验
            if (version < allowedMinVersion) {
                throw new IllegalApiVersionException(allowedMinVersion);
            }
            // 版本号从高到低向下兼容
            if (version >= this.apiVersion) {
                return this;
            }
        }

        return null;
    }

    /**
     * 定义相同条件的执行顺序：version越大越靠前
     *
     * 比如，同时存在  v1-condition 和 v2-condition时，先判断v2,再判断v1
     * 这样，如果请求为v2,则走v2接口；如果请求为v1,则v2判断不通过，再判断v1通过，于是走v1接口
     *
     * @param other
     * @param request
     * @return
     */
    @Override
    public int compareTo(ApiVersionCondition other, HttpServletRequest request) {
        return other.getApiVersion() - this.apiVersion;
    }

}
